<!DOCTYPE html>
<html style="display: none;" lang="zh">
    <head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="utf-8">
    <!--
        © Material Theme
        https://github.com/viosey/hexo-theme-material
        Version: 1.5.6 -->
    <script>
        window.materialVersion = "1.5.6"
        // Delete localstorage with these tags
        window.oldVersion = [
            'codestartv1',
            '1.3.4',
            '1.4.0',
            '1.4.0b1',
            '1.5.0',
            '1.5.2',
            '1.5.5'
        ]
    </script>

    <!-- dns prefetch -->
    <meta http-equiv="x-dns-prefetch-control" content="on">









    <link rel="dns-prefetch" href="https://fonts.googleapis.com">





    <!-- Meta & Info -->
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

    <!-- Title -->
    
    <title>
        
            做一个分布式锁的通用starter | 
        
        Noir的博客
    </title>

    <!-- Favicons -->
    <link rel="icon shortcut" type="image/ico" href="/rubin-blog/img/logo.png">
    <link rel="icon" href="/rubin-blog/img/logo.png">

    <meta name="format-detection" content="telephone=no">
    <meta name="description" itemprop="description" content="今天也要加油鸭！！！">
    <meta name="keywords" content=",redis,zookeeper,分布式资源抢占">
    <meta name="theme-color" content="#0097A7">

    <!-- Disable Fucking Bloody Baidu Tranformation -->
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta http-equiv="Cache-Control" content="no-siteapp">

    <!--[if lte IE 9]>
        <link rel="stylesheet" href="/css/ie-blocker.css">

        
            <script src="/js/ie-blocker.zhCN.js"></script>
        
    <![endif]-->

    <!-- Import lsloader -->
    <script>(function(){window.lsloader={jsRunSequence:[],jsnamemap:{},cssnamemap:{}};lsloader.removeLS=function(a){try{localStorage.removeItem(a)}catch(b){}};lsloader.setLS=function(a,c){try{localStorage.setItem(a,c)}catch(b){}};lsloader.getLS=function(a){var c="";try{c=localStorage.getItem(a)}catch(b){c=""}return c};versionString="/*"+(window.materialVersion||"unknownVersion")+"*/";lsloader.clean=function(){try{var b=[];for(var a=0;a<localStorage.length;a++){b.push(localStorage.key(a))}b.forEach(function(e){var f=lsloader.getLS(e);if(window.oldVersion){var d=window.oldVersion.reduce(function(g,h){return g||f.indexOf("/*"+h+"*/")!==-1},false);if(d){lsloader.removeLS(e)}}})}catch(c){}};lsloader.clean();lsloader.load=function(f,a,b,d){if(typeof b==="boolean"){d=b;b=undefined}d=d||false;b=b||function(){};var e;e=this.getLS(f);if(e&&e.indexOf(versionString)===-1){this.removeLS(f);this.requestResource(f,a,b,d);return}if(e){var c=e.split(versionString)[0];if(c!=a){console.log("reload:"+a);this.removeLS(f);this.requestResource(f,a,b,d);return}e=e.split(versionString)[1];if(d){this.jsRunSequence.push({name:f,code:e});this.runjs(a,f,e)}else{document.getElementById(f).appendChild(document.createTextNode(e));b()}}else{this.requestResource(f,a,b,d)}};lsloader.requestResource=function(b,e,a,c){var d=this;if(c){this.iojs(e,b,function(h,f,g){d.setLS(f,h+versionString+g);d.runjs(h,f,g)})}else{this.iocss(e,b,function(f){document.getElementById(b).appendChild(document.createTextNode(f));d.setLS(b,e+versionString+f)},a)}};lsloader.iojs=function(d,b,g){var a=this;a.jsRunSequence.push({name:b,code:""});try{var f=new XMLHttpRequest();f.open("get",d,true);f.onreadystatechange=function(){if(f.readyState==4){if((f.status>=200&&f.status<300)||f.status==304){if(f.response!=""){g(d,b,f.response);return}}a.jsfallback(d,b)}};f.send(null)}catch(c){a.jsfallback(d,b)}};lsloader.iocss=function(f,c,h,a){var b=this;try{var g=new XMLHttpRequest();g.open("get",f,true);g.onreadystatechange=function(){if(g.readyState==4){if((g.status>=200&&g.status<300)||g.status==304){if(g.response!=""){h(g.response);a();return}}b.cssfallback(f,c,a)}};g.send(null)}catch(d){b.cssfallback(f,c,a)}};lsloader.iofonts=function(f,c,h,a){var b=this;try{var g=new XMLHttpRequest();g.open("get",f,true);g.onreadystatechange=function(){if(g.readyState==4){if((g.status>=200&&g.status<300)||g.status==304){if(g.response!=""){h(g.response);a();return}}b.cssfallback(f,c,a)}};g.send(null)}catch(d){b.cssfallback(f,c,a)}};lsloader.runjs=function(f,c,e){if(!!c&&!!e){for(var b in this.jsRunSequence){if(this.jsRunSequence[b].name==c){this.jsRunSequence[b].code=e}}}if(!!this.jsRunSequence[0]&&!!this.jsRunSequence[0].code&&this.jsRunSequence[0].status!="failed"){var a=document.createElement("script");a.appendChild(document.createTextNode(this.jsRunSequence[0].code));a.type="text/javascript";document.getElementsByTagName("head")[0].appendChild(a);this.jsRunSequence.shift();if(this.jsRunSequence.length>0){this.runjs()}}else{if(!!this.jsRunSequence[0]&&this.jsRunSequence[0].status=="failed"){var d=this;var a=document.createElement("script");a.src=this.jsRunSequence[0].path;a.type="text/javascript";this.jsRunSequence[0].status="loading";a.onload=function(){d.jsRunSequence.shift();if(d.jsRunSequence.length>0){d.runjs()}};document.body.appendChild(a)}}};lsloader.tagLoad=function(b,a){this.jsRunSequence.push({name:a,code:"",path:b,status:"failed"});this.runjs()};lsloader.jsfallback=function(c,b){if(!!this.jsnamemap[b]){return}else{this.jsnamemap[b]=b}for(var a in this.jsRunSequence){if(this.jsRunSequence[a].name==b){this.jsRunSequence[a].code="";this.jsRunSequence[a].status="failed";this.jsRunSequence[a].path=c}}this.runjs()};lsloader.cssfallback=function(e,c,b){if(!!this.cssnamemap[c]){return}else{this.cssnamemap[c]=1}var d=document.createElement("link");d.type="text/css";d.href=e;d.rel="stylesheet";d.onload=d.onerror=b;var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(d,a)};lsloader.runInlineScript=function(c,b){var a=document.getElementById(b).innerText;this.jsRunSequence.push({name:c,code:a});this.runjs()}})();</script>

    <!-- Import queue -->
    <script>function Queue(){this.dataStore=[];this.offer=b;this.poll=d;this.execNext=a;this.debug=false;this.startDebug=c;function b(e){if(this.debug){console.log("Offered a Queued Function.")}if(typeof e==="function"){this.dataStore.push(e)}else{console.log("You must offer a function.")}}function d(){if(this.debug){console.log("Polled a Queued Function.")}return this.dataStore.shift()}function a(){var e=this.poll();if(e!==undefined){if(this.debug){console.log("Run a Queued Function.")}e()}}function c(){this.debug=true}}var queue=new Queue();</script>

    <!-- Import CSS -->
    
        <style id="material_css"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("material_css","/rubin-blog/css/material.min.css?Z7a72R1E4SxzBKR/WGctOA==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>
        <style id="style_css"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("style_css","/rubin-blog/css/style.min.css?H1y3uLZ/l25Ii7j0fWHmRw==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>

        

    

    

    <!-- Config CSS -->

<!-- Other Styles -->
<style>
  body, html {
    font-family: Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
    overflow-x: hidden !important;
  }
  
  code {
    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
  }

  a {
    color: #00838F;
  }

  .mdl-card__media,
  #search-label,
  #search-form-label:after,
  #scheme-Paradox .hot_tags-count,
  #scheme-Paradox .sidebar_archives-count,
  #scheme-Paradox .sidebar-colored .sidebar-header,
  #scheme-Paradox .sidebar-colored .sidebar-badge{
    background-color: #0097A7 !important;
  }

  /* Sidebar User Drop Down Menu Text Color */
  #scheme-Paradox .sidebar-colored .sidebar-nav>.dropdown>.dropdown-menu>li>a:hover,
  #scheme-Paradox .sidebar-colored .sidebar-nav>.dropdown>.dropdown-menu>li>a:focus {
    color: #0097A7 !important;
  }

  #post_entry-right-info,
  .sidebar-colored .sidebar-nav li:hover > a,
  .sidebar-colored .sidebar-nav li:hover > a i,
  .sidebar-colored .sidebar-nav li > a:hover,
  .sidebar-colored .sidebar-nav li > a:hover i,
  .sidebar-colored .sidebar-nav li > a:focus i,
  .sidebar-colored .sidebar-nav > .open > a,
  .sidebar-colored .sidebar-nav > .open > a:hover,
  .sidebar-colored .sidebar-nav > .open > a:focus,
  #ds-reset #ds-ctx .ds-ctx-entry .ds-ctx-head a {
    color: #0097A7 !important;
  }

  .toTop {
    background: #757575 !important;
  }

  .material-layout .material-post>.material-nav,
  .material-layout .material-index>.material-nav,
  .material-nav a {
    color: #757575;
  }

  #scheme-Paradox .MD-burger-layer {
    background-color: #757575;
  }

  #scheme-Paradox #post-toc-trigger-btn {
    color: #757575;
  }

  .post-toc a:hover {
    color: #00838F;
    text-decoration: underline;
  }

</style>


<!-- Theme Background Related-->

    <style>
      body{
        background-color: #F5F5F5;
      }

      /* blog_info bottom background */
      #scheme-Paradox .material-layout .something-else .mdl-card__supporting-text{
        background-color: #fff;
      }
    </style>




<!-- Fade Effect -->

    <style>
      .fade {
        transition: all 800ms linear;
        -webkit-transform: translate3d(0,0,0);
        -moz-transform: translate3d(0,0,0);
        -ms-transform: translate3d(0,0,0);
        -o-transform: translate3d(0,0,0);
        transform: translate3d(0,0,0);
        opacity: 1;
      }

      .fade.out{
        opacity: 0;
      }
    </style>


<!-- Import Font -->
<!-- Import Roboto -->

    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">


<!-- Import Material Icons -->


    <style id="material_icons"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("material_icons","/rubin-blog/css/material-icons.css?pqhB/Rd/ab0H2+kZp0RDmw==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>




    <!-- Import jQuery -->
    
        <script>lsloader.load("jq_js","/rubin-blog/js/jquery.min.js?ezyEvm8ST5CGfpA+kFFi1g==", true)</script>
    

    <!-- WebAPP Icons -->
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="application-name" content="Noir的博客">
    <meta name="msapplication-starturl" content="https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/">
    <meta name="msapplication-navbutton-color" content="#0097A7">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-title" content="Noir的博客">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link rel="apple-touch-icon" href="/rubin-blog/img/logo.png">

    <!-- Site Verification -->
    
    

    <!-- RSS -->
    

    <!-- The Open Graph protocol -->
    <meta property="og:url" content="https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/">
    <meta property="og:type" content="blog">
    <meta property="og:title" content="做一个分布式锁的通用starter | Noir的博客">
    <meta property="og:image" content="/rubin-blog/img/logo.png">
    <meta property="og:description" content="今天也要加油鸭！！！">
    <meta property="og:article:tag" content="redis"> <meta property="og:article:tag" content="zookeeper"> <meta property="og:article:tag" content="分布式资源抢占"> 

    
        <meta property="article:published_time" content="Sat Aug 22 2020 12:58:19 GMT+0800">
        <meta property="article:modified_time" content="Sat Aug 22 2020 13:43:34 GMT+0800">
    

    <!-- The Twitter Card protocol -->
    <meta name="twitter:card" content="summary_large_image">

    <!-- Add canonical link for SEO -->
    
        <link rel="canonical" href="https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html">
    

    <!-- Structured-data for SEO -->
    
        


<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "mainEntityOfPage": "https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html",
    "headline": "做一个分布式锁的通用starter",
    "datePublished": "Sat Aug 22 2020 12:58:19 GMT+0800",
    "dateModified": "Sat Aug 22 2020 13:43:34 GMT+0800",
    "author": {
        "@type": "Person",
        "name": "noir",
        "image": {
            "@type": "ImageObject",
            "url": "/img/avatar.jpg"
        },
        "description": "Freiheit als Autonomie"
    },
    "publisher": {
        "@type": "Organization",
        "name": "Noir的博客",
        "logo": {
            "@type":"ImageObject",
            "url": "/img/logo.png"
        }
    },
    "keywords": ",redis,zookeeper,分布式资源抢占",
    "description": "今天也要加油鸭！！！",
}
</script>


    

    <!-- Analytics -->
    
    
    

    <!-- Custom Head -->
    

</head>


    
        <body id="scheme-Paradox" class="lazy">
            <div class="material-layout  mdl-js-layout has-drawer is-upgraded">
                

                <!-- Main Container -->
                <main class="material-layout__content" id="main">

                    <!-- Top Anchor -->
                    <div id="top"></div>

                    
                        <!-- Hamburger Button -->
                        <button class="MD-burger-icon sidebar-toggle">
                            <span id="MD-burger-id" class="MD-burger-layer"></span>
                        </button>
                    

                    <!-- Post TOC -->

    
    <!-- Back Button -->
    <!--
    <div class="material-back" id="backhome-div" tabindex="0">
        <a class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon"
           href="#" onclick="window.history.back();return false;"
           target="_self"
           role="button"
           data-upgraded=",MaterialButton,MaterialRipple">
            <i class="material-icons" role="presentation">arrow_back</i>
            <span class="mdl-button__ripple-container">
                <span class="mdl-ripple"></span>
            </span>
        </a>
    </div>
    -->


    <!-- Left aligned menu below button -->
    
    
    <button id="post-toc-trigger-btn"
        class="mdl-button mdl-js-button mdl-button--icon">
        <i class="material-icons">format_list_numbered</i>
    </button>

    <ul class="post-toc-wrap mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect" for="post-toc-trigger-btn" style="max-height:80vh; overflow-y:scroll;">
        <ol class="post-toc"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#简介"><span class="post-toc-number">1.</span> <span class="post-toc-text">简介</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#项目构建"><span class="post-toc-number">2.</span> <span class="post-toc-text">项目构建</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#starter配置类"><span class="post-toc-number">2.1.</span> <span class="post-toc-text">starter配置类</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#定义接口"><span class="post-toc-number">2.2.</span> <span class="post-toc-text">定义接口</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#实现aop注解支持"><span class="post-toc-number">2.3.</span> <span class="post-toc-text">实现aop注解支持</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#实现默认实现与相应的配置化"><span class="post-toc-number">2.4.</span> <span class="post-toc-text">实现默认实现与相应的配置化</span></a></li></ol></li></ol>
    </ul>
    




<!-- Layouts -->

    <!-- Post Module -->
    <div class="material-post_container">

        <div class="material-post mdl-grid">
            <div class="mdl-card mdl-shadow--4dp mdl-cell mdl-cell--12-col">

                <!-- Post Header(Thumbnail & Title) -->
                
    <!-- Paradox Post Header -->
    
        
            <!-- Random Thumbnail -->
            <div class="post_thumbnail-random mdl-card__media mdl-color-text--grey-50">
            <script type="text/ls-javascript" id="post-thumbnail-script">
    var randomNum = Math.floor(Math.random() * 19 + 1);

    $('.post_thumbnail-random').attr('data-original', '/rubin-blog/img/random/material-' + randomNum + '.png');
    $('.post_thumbnail-random').addClass('lazy');
</script>

        
    
            <p class="article-headline-p">
                做一个分布式锁的通用starter
            </p>
        </div>





                
                    <!-- Paradox Post Info -->
                    <div class="mdl-color-text--grey-700 mdl-card__supporting-text meta">

    <!-- Author Avatar -->
    <div id="author-avatar">
        <img src="/rubin-blog/img/avatar.jpg" width="44px" height="44px" alt="Author Avatar"/>
    </div>
    <!-- Author Name & Date -->
    <div>
        <strong>noir</strong>
        <span>8月 22, 2020</span>
    </div>

    <div class="section-spacer"></div>

    <!-- Favorite -->
    <!--
        <button id="article-functions-like-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon btn-like">
            <i class="material-icons" role="presentation">favorite</i>
            <span class="visuallyhidden">favorites</span>
        </button>
    -->

    <!-- Qrcode -->
    

    <!-- Tags (bookmark) -->
    
    <button id="article-functions-viewtags-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon">
        <i class="material-icons" role="presentation">bookmark</i>
        <span class="visuallyhidden">bookmark</span>
    </button>
    <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="article-functions-viewtags-button">
        <li class="mdl-menu__item">
        <a class="post_tag-link" href="/rubin-blog/tags/redis/">redis</a></li><li class="mdl-menu__item"><a class="post_tag-link" href="/rubin-blog/tags/zookeeper/">zookeeper</a></li><li class="mdl-menu__item"><a class="post_tag-link" href="/rubin-blog/tags/分布式资源抢占/">分布式资源抢占</a>
    </ul>
    

    <!-- Share -->
    
        <button id="article-fuctions-share-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon">
    <i class="material-icons" role="presentation">share</i>
    <span class="visuallyhidden">share</span>
</button>
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="article-fuctions-share-button">
    

    

    <!-- Share Weibo -->
    
        <a class="post_share-link" href="http://service.weibo.com/share/share.php?appkey=&title=做一个分布式锁的通用starter&url=https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html&pic=https://achacha.gitee.io/rubin-blog/img/logo.png&searchPic=false&style=simple" target="_blank">
            <li class="mdl-menu__item">
                分享到微博
            </li>
        </a>
    

    <!-- Share Twitter -->
    
        <a class="post_share-link" href="https://twitter.com/intent/tweet?text=做一个分布式锁的通用starter&url=https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html&via=noir" target="_blank">
            <li class="mdl-menu__item">
                分享到 Twitter
            </li>
        </a>
    

    <!-- Share Facebook -->
    
        <a class="post_share-link" href="https://www.facebook.com/sharer/sharer.php?u=https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html" target="_blank">
            <li class="mdl-menu__item">
                分享到 Facebook
            </li>
        </a>
    

    <!-- Share Google+ -->
    
        <a class="post_share-link" href="https://plus.google.com/share?url=https://achacha.gitee.io/rubin-blog/rubin-blog/2020/08/22/做一个分布式锁的通用starter/index.html" target="_blank">
            <li class="mdl-menu__item">
                分享到 Google+
            </li>
        </a>
    

    <!-- Share LinkedIn -->
    

    <!-- Share QQ -->
    

    <!-- Share Telegram -->
    
</ul>

    
</div>

                

                <!-- Post Content -->
                <div id="post-content" class="mdl-color-text--grey-700 mdl-card__supporting-text fade out">
    
        <h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>接上篇  <a href="https://noir-lattice.github.io/2020/07/19/%E7%BB%86%E8%B0%88%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" target="_blank" rel="noopener">细谈分布式锁</a>, 实现通用的一个spring boot starter。</p>
<h2 id="项目构建"><a href="#项目构建" class="headerlink" title="项目构建"></a>项目构建</h2><p>构建完成的项目包结构如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">├─src</span><br><span class="line">    ├─main.java.com.noir.common.lock</span><br><span class="line">       │                          ├─annotation    ## 注解</span><br><span class="line">       │                          ├─aop           ## aop切面实现注解支持</span><br><span class="line">       │                          ├─excptions     ## 异常都在这里</span><br><span class="line">       │                          ├─impl       </span><br><span class="line">       │                          │  ├─factorys   ## 锁的工厂实现类</span><br><span class="line">       │                          │  └─locks      ## 锁的实现类</span><br><span class="line">       │                          └─properties    ## 中间件配置的封装</span><br><span class="line">       └─resources</span><br><span class="line">           └─META-INF</span><br></pre></td></tr></table></figure>

<h3 id="starter配置类"><a href="#starter配置类" class="headerlink" title="starter配置类"></a>starter配置类</h3><p>配置提供starter发现的META-INF/spring.factories</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.noir.common.LockStarterAutoConfiguration</span><br></pre></td></tr></table></figure>

<p>配置类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.properties.DLockProperties;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.properties.RedLockProperties;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.properties.RedisDLockProperties;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.properties.ZookeeperDLockProperties;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.Watcher;</span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.ZooKeeper;</span><br><span class="line"><span class="keyword">import</span> org.redisson.Redisson;</span><br><span class="line"><span class="keyword">import</span> org.redisson.api.RedissonClient;</span><br><span class="line"><span class="keyword">import</span> org.redisson.config.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.context.properties.EnableConfigurationProperties;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.ComponentScan;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.util.StringUtils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.CountDownLatch;</span><br><span class="line"><span class="keyword">import</span> java.util.stream.Collectors;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * starter配置启动类</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 扫描了 &#123;<span class="doctag">@link</span> com.noir.common.lock&#125; 下的</span></span><br><span class="line"><span class="comment"> * 所有类进行初始化，包含aop，对应配置的工程类，</span></span><br><span class="line"><span class="comment"> * 对应中间件配置以及封装使用的service</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 仅在 &#123;<span class="doctag">@code</span> locker.enabled=true&#125; 时有效</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 具体实现参考接口及拓展类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.DLockFactory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.LockableService</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.ReentrantDLock</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf</span>4j</span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan</span>(<span class="string">"com.noir.common.lock"</span>)</span><br><span class="line"><span class="meta">@ConditionalOnProperty</span>(prefix = <span class="string">"locker"</span>, value = <span class="string">"enabled"</span>, havingValue = <span class="string">"true"</span>)</span><br><span class="line"><span class="meta">@EnableConfigurationProperties</span>(&#123;DLockProperties<span class="class">.<span class="keyword">class</span>, <span class="title">RedisDLockProperties</span>.<span class="title">class</span>, <span class="title">RedLockProperties</span>.<span class="title">class</span>, <span class="title">ZookeeperDLockProperties</span>.<span class="title">class</span>&#125;)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">LockStarterAutoConfiguration</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span>(required = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> RedisDLockProperties redisDLockProperties;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span>(required = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> RedLockProperties redLockProperties;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span>(required = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> ZookeeperDLockProperties zookeeperDLockProperties;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty</span>(prefix = <span class="string">"locker"</span>, value = <span class="string">"type"</span>, havingValue = <span class="string">"redis-expire"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedissonClient <span class="title">redissonEXClient</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> createRedissonClient(redisDLockProperties);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty</span>(prefix = <span class="string">"locker"</span>, value = <span class="string">"type"</span>, havingValue = <span class="string">"redis-get-set"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedissonClient <span class="title">redissonGetSetClient</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> createRedissonClient(redisDLockProperties);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty</span>(prefix = <span class="string">"locker"</span>, value = <span class="string">"type"</span>, havingValue = <span class="string">"red-lock"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;RedissonClient&gt; <span class="title">redissonRedLockClients</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> redLockProperties.getClients().stream().map(<span class="keyword">this</span>::createRedissonClient).collect(Collectors.toList());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty</span>(prefix = <span class="string">"locker"</span>, value = <span class="string">"type"</span>, havingValue = <span class="string">"zookeeper"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> ZooKeeper <span class="title">zooKeeper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> createZooKeeper(zookeeperDLockProperties);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> ZooKeeper <span class="title">createZooKeeper</span><span class="params">(ZookeeperDLockProperties zookeeperDLockProperties)</span> </span>&#123;</span><br><span class="line">        ZooKeeper zooKeeper = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">final</span> CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">1</span>);</span><br><span class="line">            zooKeeper = <span class="keyword">new</span> ZooKeeper(zookeeperDLockProperties.getAddress(), zookeeperDLockProperties.getTimeout(), event -&gt; &#123;</span><br><span class="line">                <span class="keyword">if</span>(Watcher.Event.KeeperState.SyncConnected==event.getState())&#123;</span><br><span class="line">                    <span class="comment">//如果收到了服务端的响应事件,连接成功</span></span><br><span class="line">                    countDownLatch.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">            countDownLatch.await();</span><br><span class="line">            log.info(<span class="string">"【初始化ZooKeeper连接状态....】=&#123;&#125;"</span>, zooKeeper.getState());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            log.error(<span class="string">"【初始化ZooKeeper连接异常....】= &#123;&#125;"</span>, e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> zooKeeper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> RedissonClient <span class="title">createRedissonClient</span><span class="params">(RedisDLockProperties redisDLockProperties)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (redisDLockProperties.getMode()) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"single"</span>:</span><br><span class="line">                <span class="keyword">return</span> redissonSingle(redisDLockProperties);</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"cluster"</span>:</span><br><span class="line">                <span class="keyword">return</span> redissonCluster(redisDLockProperties);</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sentinel"</span>:</span><br><span class="line">                <span class="keyword">return</span> redissonSentinel(redisDLockProperties);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 单机模式 redisson 客户端</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> RedissonClient <span class="title">redissonSingle</span><span class="params">(RedisDLockProperties redisDLockProperties)</span> </span>&#123;</span><br><span class="line">        Config config = <span class="keyword">new</span> Config();</span><br><span class="line">        String node = redisDLockProperties.getSingle().getAddress();</span><br><span class="line">        node = node.startsWith(<span class="string">"redis://"</span>) ? node : <span class="string">"redis://"</span> + node;</span><br><span class="line">        SingleServerConfig serverConfig = config.useSingleServer()</span><br><span class="line">                .setAddress(node)</span><br><span class="line">                .setTimeout(redisDLockProperties.getSingle().getConnTimeout())</span><br><span class="line">                .setConnectionPoolSize(redisDLockProperties.getPool().getSize())</span><br><span class="line">                .setConnectionMinimumIdleSize(redisDLockProperties.getPool().getMinIdle());</span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.isEmpty(redisDLockProperties.getPassword())) &#123;</span><br><span class="line">            serverConfig.setPassword(redisDLockProperties.getPassword());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Redisson.create(config);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 集群模式的 redisson 客户端</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> RedissonClient <span class="title">redissonCluster</span><span class="params">(RedisDLockProperties redisDLockProperties)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"cluster redisProperties:"</span> + redisDLockProperties.getCluster());</span><br><span class="line"></span><br><span class="line">        Config config = <span class="keyword">new</span> Config();</span><br><span class="line">        String[] nodes = extractRedisAddressByString(redisDLockProperties.getCluster().getNodes());</span><br><span class="line"></span><br><span class="line">        ClusterServersConfig serverConfig = config.useClusterServers()</span><br><span class="line">                .addNodeAddress(nodes)</span><br><span class="line">                .setScanInterval(</span><br><span class="line">                        redisDLockProperties.getCluster().getScanInterval())</span><br><span class="line">                .setIdleConnectionTimeout(</span><br><span class="line">                        redisDLockProperties.getPool().getSoTimeout())</span><br><span class="line">                .setConnectTimeout(</span><br><span class="line">                        redisDLockProperties.getPool().getConnTimeout())</span><br><span class="line">                .setFailedAttempts(</span><br><span class="line">                        redisDLockProperties.getCluster().getFailedAttempts())</span><br><span class="line">                .setRetryAttempts(</span><br><span class="line">                        redisDLockProperties.getCluster().getRetryAttempts())</span><br><span class="line">                .setRetryInterval(</span><br><span class="line">                        redisDLockProperties.getCluster().getRetryInterval())</span><br><span class="line">                .setMasterConnectionPoolSize(redisDLockProperties.getCluster()</span><br><span class="line">                        .getMasterConnectionPoolSize())</span><br><span class="line">                .setSlaveConnectionPoolSize(redisDLockProperties.getCluster()</span><br><span class="line">                        .getSlaveConnectionPoolSize())</span><br><span class="line">                .setTimeout(redisDLockProperties.getTimeout());</span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.isEmpty(redisDLockProperties.getPassword())) &#123;</span><br><span class="line">            serverConfig.setPassword(redisDLockProperties.getPassword());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Redisson.create(config);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 哨兵模式 redisson 客户端</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> RedissonClient <span class="title">redissonSentinel</span><span class="params">(RedisDLockProperties redisDLockProperties)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"sentinel redisProperties:"</span> + redisDLockProperties.getSentinel());</span><br><span class="line">        Config config = <span class="keyword">new</span> Config();</span><br><span class="line"></span><br><span class="line">        String[] nodes = extractRedisAddressByString(redisDLockProperties.getSentinel().getNodes());</span><br><span class="line"></span><br><span class="line">        SentinelServersConfig serverConfig = config.useSentinelServers()</span><br><span class="line">                .addSentinelAddress(nodes)</span><br><span class="line">                .setMasterName(redisDLockProperties.getSentinel().getMaster())</span><br><span class="line">                .setReadMode(ReadMode.SLAVE)</span><br><span class="line">                .setFailedAttempts(redisDLockProperties.getSentinel().getFailMax())</span><br><span class="line">                .setTimeout(redisDLockProperties.getTimeout())</span><br><span class="line">                .setMasterConnectionPoolSize(redisDLockProperties.getPool().getMasterConnectionPoolSize())</span><br><span class="line">                .setSlaveConnectionPoolSize(redisDLockProperties.getPool().getSlaveConnectionPoolSize());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.isEmpty(redisDLockProperties.getPassword())) &#123;</span><br><span class="line">            serverConfig.setPassword(redisDLockProperties.getPassword());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Redisson.create(config);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String[] extractRedisAddressByString(String nodeStr) &#123;</span><br><span class="line">        String[] nodes = nodeStr.split(<span class="string">","</span>);</span><br><span class="line">        <span class="keyword">return</span> Arrays.stream(nodes)</span><br><span class="line">                        .map(str -&gt; str.startsWith(<span class="string">"redis://"</span>) ? str : <span class="string">"redis://"</span> + str)</span><br><span class="line">                        .toArray(String[]::<span class="keyword">new</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="定义接口"><a href="#定义接口" class="headerlink" title="定义接口"></a>定义接口</h3><ul>
<li><p>提供DLockFactory暴露给外部，实现一定的灵活性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * DLock工厂</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 提供了DLock的工厂接口，为了更通用并可拓展，</span></span><br><span class="line"><span class="comment"> * 对 &#123;<span class="doctag">@link</span> ReentrantDLock&#125; 进行了擦除，</span></span><br><span class="line"><span class="comment"> * 在进行具体自定义拓展时，建议继承ReentrantDLock</span></span><br><span class="line"><span class="comment"> * 以实现可重入支持。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 具体工厂实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.factorys.RedisSetNXExpireLockFactory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.factorys.RedisSetNXGetSetLockFactory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.factorys.RedLockFactory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.factorys.ZookeeperLockFactory</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">DLockFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取锁</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> name 锁名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 锁对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Lock <span class="title">getLock</span><span class="params">(String name)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取锁</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> name 锁名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> expire 过去时间</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> unit 时间单位</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 锁对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Lock <span class="title">getLock</span><span class="params">(String name, <span class="keyword">long</span> expire, TimeUnit unit)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>提供LockableService作为方便使用Bean接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Callable;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 具有加锁逻辑的服务接口</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 提供了lambda表达式的上锁支持，标定锁名称</span></span><br><span class="line"><span class="comment"> * 与对应的Callable或者Runnable即可实现对</span></span><br><span class="line"><span class="comment"> * 块级逻辑的资源锁定，对于多个资源的锁定可以</span></span><br><span class="line"><span class="comment"> * 通过多级嵌套进行实现，同样的，因为默认实现</span></span><br><span class="line"><span class="comment"> * 的工程均实现了可重入接口 &#123;<span class="doctag">@link</span> ReentrantDLock&#125;</span></span><br><span class="line"><span class="comment"> * 在未使用自定义的DLockFactory时均可重入</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 具体的实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.LockableServiceImpl</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">LockableService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据key进行加锁, 执行callable任务</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key      加锁的键值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> callable 执行的操作</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;T&gt;      callable任务返回的结果类型</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> callable任务返回的结果</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception callable任务执行过程中产生的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    &lt;T&gt; <span class="function">T <span class="title">lockAndExecute</span><span class="params">(String key, Callable&lt;T&gt; callable)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据key进行加锁，执行runnable任务</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key      加锁的键值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> runnable 执行的操作</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception runnable任务执行过程中产生的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">lockAndExecute</span><span class="params">(String key, Runnable runnable)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>提供可重入锁的基础类支持</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.Objects;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 可重入DLock拓展</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 使用thread local实现锁状态标定，如拓展实现</span></span><br><span class="line"><span class="comment"> * 自定义的锁实现，请务必在解锁后 &#123;<span class="doctag">@link</span> ReentrantDLock#exit(String)&#125;</span></span><br><span class="line"><span class="comment"> * 以保证状态的清除</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 提供的四种实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.locks.RedLockWrapper</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.locks.RedisSetNXGetSetLock</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.locks.RedisSetNXExpireLock</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.impl.locks.ZookeeperLock</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ReentrantDLock</span> <span class="keyword">implements</span> <span class="title">Lock</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> ThreadLocal&lt;List&lt;String&gt;&gt; localLocks = <span class="keyword">new</span> ThreadLocal&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isEntered</span><span class="params">(String lockName)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; locks = localLocks.get();</span><br><span class="line">        <span class="keyword">if</span> (Objects.isNull(locks)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> locks.contains(lockName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">enter</span><span class="params">(String lockName)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; locks = localLocks.get();</span><br><span class="line">        <span class="keyword">if</span> (Objects.isNull(locks)) &#123;</span><br><span class="line">            localLocks.set(<span class="keyword">new</span> ArrayList&lt;&gt;());</span><br><span class="line">            locks = localLocks.get();</span><br><span class="line">        &#125;</span><br><span class="line">        locks.add(lockName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">exit</span><span class="params">(String lockName)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; locks = localLocks.get();</span><br><span class="line">        <span class="keyword">if</span> (Objects.nonNull(locks)) &#123;</span><br><span class="line">            locks.remove(lockName);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="实现aop注解支持"><a href="#实现aop注解支持" class="headerlink" title="实现aop注解支持"></a>实现aop注解支持</h3><ul>
<li><p>定义一个注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock.annotation;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 注解式分布式锁支持</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 在标定的方法执行结束后将自动释放锁，同样的，</span></span><br><span class="line"><span class="comment"> * 支持可重入意味着可以嵌套使用而不发生死锁。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> ElementType.ANNOTATION_TYPE&#125;，</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> <span class="doctag">@Inherited</span>&#125;</span></span><br><span class="line"><span class="comment"> * 这意味着这同样是一个元注解，可以像普通使用</span></span><br><span class="line"><span class="comment"> * Spring annotation一样将多个注解标定到一</span></span><br><span class="line"><span class="comment"> * 个通用的注解上并使用</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> org.springframework.core.annotation.AliasFor&#125;</span></span><br><span class="line"><span class="comment"> * 来指定annotation并将value标定merge进来。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> ElementType.TYPE&#125;</span></span><br><span class="line"><span class="comment"> * 而对于标定在class上的注解，都将会尝试解析</span></span><br><span class="line"><span class="comment"> * 锁并在其下的方法执行过程中上锁，我认为这不</span></span><br><span class="line"><span class="comment"> * 是一个好方法，对于整体的逻辑和类内部的接口</span></span><br><span class="line"><span class="comment"> * 设计都是一个限制，但我还是提供了，请注意。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 同所有Spring AOP标定的注解一样，他可能存</span></span><br><span class="line"><span class="comment"> * 在失效的场景，即内部互相调用时会失去作用，</span></span><br><span class="line"><span class="comment"> * 这会带来额外的心智负担，如果你的团队对于这</span></span><br><span class="line"><span class="comment"> * 些方面并不熟悉，请更多使用</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> com.noir.common.lock.LockableService&#125;</span></span><br><span class="line"><span class="comment"> * or</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> com.noir.common.lock.DLockFactory&#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 如果还有疑问及迷惑，可以查看具体的aop实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.aop.DLockAnnotationAdvice</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.aop.DLockClassAnnotationAdvisor</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.aop.DLockMethodAnnotationAdvisor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Target</span>(&#123;ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE&#125;)</span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> DLock &#123;</span><br><span class="line">    String[] value();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">timeOutSecond</span><span class="params">()</span> <span class="keyword">default</span> 30</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>对于类上标记注解的切面支持</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock.aop;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.annotation.DLock;</span><br><span class="line"><span class="keyword">import</span> org.aopalliance.aop.Advice;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.Pointcut;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.support.AbstractPointcutAdvisor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.support.annotation.AnnotationMatchingPointcut;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 类注解advisor</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 用于标定类上有 &#123;<span class="doctag">@link</span> com.noir.common.lock.annotation.DLock&#125;</span></span><br><span class="line"><span class="comment"> * 的切点与对应AnnotationAdvice</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.aop.DLockAnnotationAdvice</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DLockClassAnnotationAdvisor</span> <span class="keyword">extends</span> <span class="title">AbstractPointcutAdvisor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DLockAnnotationAdvice dLockAnnotationAdvice;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Pointcut <span class="title">getPointcut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> AnnotationMatchingPointcut(DLock<span class="class">.<span class="keyword">class</span>, <span class="title">null</span>, <span class="title">true</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Advice <span class="title">getAdvice</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> dLockAnnotationAdvice;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>对于方法上标记注解的切面支持</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock.aop;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.annotation.DLock;</span><br><span class="line"><span class="keyword">import</span> org.aopalliance.aop.Advice;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.Pointcut;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.support.AbstractPointcutAdvisor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.aop.support.annotation.AnnotationMatchingPointcut;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 方法注解advisor</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 用于标定方法上有 &#123;<span class="doctag">@link</span> com.noir.common.lock.annotation.DLock&#125;</span></span><br><span class="line"><span class="comment"> * 的切点与对应AnnotationAdvice</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.aop.DLockAnnotationAdvice</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DLockMethodAnnotationAdvisor</span> <span class="keyword">extends</span> <span class="title">AbstractPointcutAdvisor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DLockAnnotationAdvice dLockAnnotationAdvice;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Pointcut <span class="title">getPointcut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> AnnotationMatchingPointcut(<span class="keyword">null</span>, DLock<span class="class">.<span class="keyword">class</span>, <span class="title">true</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Advice <span class="title">getAdvice</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> dLockAnnotationAdvice;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>具体实现的advice</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.noir.common.lock.aop;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.DLockFactory;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.annotation.DLock;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.excptions.ErrorParseLockKey;</span><br><span class="line"><span class="keyword">import</span> com.noir.common.lock.excptions.TryLockFailException;</span><br><span class="line"><span class="keyword">import</span> org.aopalliance.intercept.MethodInterceptor;</span><br><span class="line"><span class="keyword">import</span> org.aopalliance.intercept.MethodInvocation;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.expression.BeanFactoryResolver;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.LocalVariableTableParameterNameDiscoverer;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.AnnotatedElementUtils;</span><br><span class="line"><span class="keyword">import</span> org.springframework.expression.ExpressionParser;</span><br><span class="line"><span class="keyword">import</span> org.springframework.expression.ParseException;</span><br><span class="line"><span class="keyword">import</span> org.springframework.expression.common.TemplateParserContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.expression.spel.standard.SpelExpressionParser;</span><br><span class="line"><span class="keyword">import</span> org.springframework.expression.spel.support.StandardEvaluationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"><span class="keyword">import</span> org.springframework.util.StringUtils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.util.LinkedList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.Objects;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 具体的注解解析advice</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 实现拓展了 &#123;<span class="doctag">@link</span> MethodInterceptor&#125; 以支持</span></span><br><span class="line"><span class="comment"> * 对于方法的上锁增强，通过查找方法及其类上的注解对</span></span><br><span class="line"><span class="comment"> * 相应资源进行上锁或等待。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 拓展提供了对方法参数的SpEL解析，可以通过如</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> <span class="doctag">@DLock</span>("#&#123;#xxx&#125;")&#125; 来获取传入参</span></span><br><span class="line"><span class="comment"> * 数的解析与资源的上锁</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 资源锁依赖 &#123;<span class="doctag">@link</span> DLockFactory&#125;来获取实现了</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> Lock&#125; 的实例并对资源进行后续操作，如有</span></span><br><span class="line"><span class="comment"> * 疑惑可看工厂类的接口及其下实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> com.noir.common.lock.DLockFactory</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DLockAnnotationAdvice</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DLockFactory lockFactory;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ApplicationContext applicationContext;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// method params discover</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = <span class="keyword">new</span> LocalVariableTableParameterNameDiscoverer();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(MethodInvocation invocation)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> doLock(invocation);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Object <span class="title">doLock</span><span class="params">(MethodInvocation invocation)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        Object[] args = invocation.getArguments();</span><br><span class="line">        Method method = invocation.getMethod();</span><br><span class="line">        Class&lt;?&gt; clz = method.getDeclaringClass();</span><br><span class="line"></span><br><span class="line">        DLock dLock = AnnotatedElementUtils.findMergedAnnotation(clz, DLock<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        DLock methodDLock = AnnotatedElementUtils.findMergedAnnotation(method, DLock<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        <span class="keyword">if</span> (Objects.nonNull(methodDLock)) &#123;</span><br><span class="line">            dLock = methodDLock;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">assert</span> dLock != <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建解析器</span></span><br><span class="line">        ExpressionParser parser = <span class="keyword">new</span> SpelExpressionParser();</span><br><span class="line">        <span class="comment">// 创建上下文</span></span><br><span class="line">        StandardEvaluationContext ctx = creteCtx(method, args);</span><br><span class="line"></span><br><span class="line">        List&lt;Lock&gt; locks = <span class="keyword">new</span> LinkedList&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (String lockResourceEL : dLock.value()) &#123;</span><br><span class="line">            <span class="comment">// el parse</span></span><br><span class="line">            String lockResource = parseKey(parser, ctx, lockResourceEL);</span><br><span class="line">            locks.add(lockFactory.getLock(lockResource));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Lock lock:locks) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!lock.tryLock(dLock.timeOutSecond(), TimeUnit.SECONDS)) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> TryLockFailException();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> invocation.proceed();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Lock lock:locks) &#123;</span><br><span class="line">                <span class="keyword">if</span> (lock != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    lock.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * SpEL解析</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">parseKey</span><span class="params">(ExpressionParser parser, StandardEvaluationContext ctx, String key)</span> <span class="keyword">throws</span> ErrorParseLockKey </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (StringUtils.isEmpty(key)) <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> parser.parseExpression(key, <span class="keyword">new</span> TemplateParserContext()).getValue(ctx, String<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ParseException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ErrorParseLockKey();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 初始化解析上下文</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> StandardEvaluationContext <span class="title">creteCtx</span><span class="params">(Method method, Object[] args)</span> </span>&#123;</span><br><span class="line">        String[] paraNameArr = parameterNameDiscoverer.getParameterNames(method);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//SpEL上下文</span></span><br><span class="line">        StandardEvaluationContext context = <span class="keyword">new</span> StandardEvaluationContext();</span><br><span class="line">        <span class="comment">//把方法参数放入SpEL上下文中</span></span><br><span class="line">        <span class="keyword">if</span> (Objects.nonNull(paraNameArr)) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; paraNameArr.length; i++) &#123;</span><br><span class="line">                context.setVariable(paraNameArr[i], args[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// bean ctx support</span></span><br><span class="line">        context.setBeanResolver(<span class="keyword">new</span> BeanFactoryResolver(applicationContext));</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="实现默认实现与相应的配置化"><a href="#实现默认实现与相应的配置化" class="headerlink" title="实现默认实现与相应的配置化"></a>实现默认实现与相应的配置化</h3><p>在上面所说的项目结构指定的包中，几种实现更多是基于上篇所述，有兴趣的可以移步至github: <a href="https://github.com/noir-lattice/spring-boot-distributed-lock-starter" target="_blank" rel="noopener">spring-boot-distributed-lock-starter</a></p>

        
    

    
</div>


                

                <!-- Post Comments -->
                
                    
                
            </div>

            <!-- Post Prev & Next Nav -->
            <nav class="material-nav mdl-color-text--grey-50 mdl-cell mdl-cell--12-col">
    <!-- Prev Nav -->
    
        <a href="/rubin-blog/2020/10/01/Spring-Cloud-Gateway接入nacos动态路由刷新/" id="post_nav-newer" class="prev-content">
            <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon mdl-color--white mdl-color-text--grey-900" role="presentation">
                <i class="material-icons">arrow_back</i>
            </button>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            新篇
        </a>
    

    <!-- Section Spacer -->
    <div class="section-spacer"></div>

    <!-- Next Nav -->
    
        <a href="/rubin-blog/2020/07/19/细谈分布式锁/" id="post_nav-older" class="next-content">
            旧篇
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon mdl-color--white mdl-color-text--grey-900" role="presentation">
                <i class="material-icons">arrow_forward</i>
            </button>
        </a>
    
</nav>

        </div>
    </div>



                    
                        <!-- Overlay For Active Sidebar -->
<div class="sidebar-overlay"></div>

<!-- Material sidebar -->
<aside id="sidebar" class="sidebar sidebar-colored sidebar-fixed-left" role="navigation">
    <div id="sidebar-main">
        <!-- Sidebar Header -->
        <div class="sidebar-header header-cover" style="background-image: url(/rubin-blog/img/sidebar_header.png);">
    <!-- Top bar -->
    <div class="top-bar"></div>

    <!-- Sidebar toggle button -->
    <button type="button" class="sidebar-toggle mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon" style="display: initial;" data-upgraded=",MaterialButton,MaterialRipple">
        <i class="material-icons">clear_all</i>
        <span class="mdl-button__ripple-container">
            <span class="mdl-ripple">
            </span>
        </span>
    </button>

    <!-- Sidebar Avatar -->
    <div class="sidebar-image">
        <img src="/rubin-blog/img/avatar.jpg" alt="noir's avatar">
    </div>

    <!-- Sidebar Email -->
    <a data-toggle="dropdown" class="sidebar-brand" href="#settings-dropdown">
        noir-lattice@outlook.com
        <b class="caret"></b>
    </a>
</div>


        <!-- Sidebar Navigation  -->
        <ul class="nav sidebar-nav">
    <!-- User dropdown  -->
    <li class="dropdown">
        <ul id="settings-dropdown" class="dropdown-menu">
            
                <li>
                    <a href="#" target="_blank" title="Email Me">
                        
                            <i class="material-icons sidebar-material-icons sidebar-indent-left1pc-element">email</i>
                        
                        Email Me
                    </a>
                </li>
            
        </ul>
    </li>

    <!-- Homepage -->
    
        <li id="sidebar-first-li">
            <a href="/rubin-blog/">
                
                    <i class="material-icons sidebar-material-icons">home</i>
                
                主页
            </a>
        </li>
        
    

    <!-- Archives  -->
    
        <li class="dropdown">
            <a href="#" class="ripple-effect dropdown-toggle" data-toggle="dropdown">
                
                    <i class="material-icons sidebar-material-icons">inbox</i>
                
                    归档
                <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
            <li>
                <a class="sidebar_archives-link" href="/rubin-blog/archives/2020/10/">十月 2020<span class="sidebar_archives-count">3</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2020/08/">八月 2020<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2020/07/">七月 2020<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2020/05/">五月 2020<span class="sidebar_archives-count">6</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2019/11/">十一月 2019<span class="sidebar_archives-count">3</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2019/10/">十月 2019<span class="sidebar_archives-count">2</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2019/09/">九月 2019<span class="sidebar_archives-count">2</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/archives/2019/08/">八月 2019<span class="sidebar_archives-count">5</span></a>
            </ul>
        </li>
        
    

    <!-- Categories  -->
    
        <li class="dropdown">
            <a href="#" class="ripple-effect dropdown-toggle" data-toggle="dropdown">
                
                    <i class="material-icons sidebar-material-icons">chrome_reader_mode</i>
                
                分类
                <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
                <li>
                <a class="sidebar_archives-link" href="/rubin-blog/categories/devops/">devops<span class="sidebar_archives-count">4</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/java/">java<span class="sidebar_archives-count">4</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/kubernetes/">kubernetes<span class="sidebar_archives-count">3</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/中间件/">中间件<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/杂谈/">杂谈<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/架构/">架构<span class="sidebar_archives-count">4</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/算法与数据结构/">算法与数据结构<span class="sidebar_archives-count">3</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/缓存中间件/">缓存中间件<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/网络/">网络<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/rubin-blog/categories/面试/">面试<span class="sidebar_archives-count">1</span></a>
            </ul>
        </li>
        
    

    <!-- Pages  -->
    

    <!-- Article Number  -->
    
        <li>
            <a href="/archives">
                文章总数
                <span class="sidebar-badge">23</span>
            </a>
        </li>
        
    
</ul>


        <!-- Sidebar Footer -->
        <!--
I'm glad you use this theme, the development is no so easy, I hope you can keep the copyright, I will thank you so much.
If you still want to delete the copyrights, could you still retain the first one? Which namely "Theme Material"
It will not impact the appearance and can give developers a lot of support :)

很高兴您使用并喜欢该主题，开发不易 十分谢谢与希望您可以保留一下版权声明。
如果您仍然想删除的话 能否只保留第一项呢？即 "Theme Material"
它不会影响美观并可以给开发者很大的支持和动力。 :)
-->

<!-- Sidebar Divider -->

    <div class="sidebar-divider"></div>


<!-- Theme Material -->
<!-- 
    <a href="https://github.com/viosey/hexo-theme-material"  class="sidebar-footer-text-a" target="_blank">
        <div class="sidebar-text mdl-button mdl-js-button mdl-js-ripple-effect sidebar-footer-text-div" data-upgraded=",MaterialButton,MaterialRipple">
            主题 - Material
            <span class="sidebar-badge badge-circle">i</span>
        </div>
    </a>
 -->

<!-- Help & Support -->
<!--

-->

<!-- Feedback -->
<!--

-->

<!-- About Theme -->
<!--

-->

    </div>

    <!-- Sidebar Image -->
    

</aside>

                    

                    
                        <!-- Footer Top Button -->
                        <div id="back-to-top" class="toTop-wrap">
    <a href="#top" class="toTop">
        <i class="material-icons footer_top-i">expand_less</i>
    </a>
</div>

                    

                    <!--Footer-->
<footer class="mdl-mini-footer" id="bottom">
    
        <!-- Paradox Footer Left Section -->
        <div class="mdl-mini-footer--left-section sns-list">
    <!-- Twitter -->
    

    <!-- Facebook -->
    

    <!-- Google + -->
    

    <!-- Weibo -->
    

    <!-- Instagram -->
    

    <!-- Tumblr -->
    

    <!-- Github -->
    
        <a href="https://github.com/noir-lattice" target="_blank">
            <button class="mdl-mini-footer--social-btn social-btn footer-sns-github">
                <span class="visuallyhidden">Github</span>
            </button><!--
     --></a>
    

    <!-- LinkedIn -->
    

    <!-- Zhihu -->
    

    <!-- Bilibili -->
    
        <a href="https://space.bilibili.com/2006225" target="_blank">
            <button class="mdl-mini-footer--social-btn social-btn footer-sns-bilibili">
                <span class="visuallyhidden">Bilibili</span>
            </button><!--
     --></a>
    

    <!-- Telegram -->
    

    <!-- V2EX -->
    

    <!-- Segmentfault -->
    
</div>


        <!--Copyright-->
        <div id="copyright">
            Copyright&nbsp;©&nbsp;<span year></span>&nbsp;Noir的博客
            
        </div>

        <!-- Paradox Footer Right Section -->

        <!--
        I am glad you use this theme, the development is no so easy, I hope you can keep the copyright.
        It will not impact the appearance and can give developers a lot of support :)

        很高兴您使用该主题，开发不易，希望您可以保留一下版权声明。
        它不会影响美观并可以给开发者很大的支持。 :)
        -->

        <div class="mdl-mini-footer--right-section">
            <div>
                <div class="footer-develop-div">Powered by <a href="https://hexo.io" target="_blank" class="footer-develop-a">Hexo</a></div>
                <div class="footer-develop-div">Theme - <a href="https://github.com/viosey/hexo-theme-material" target="_blank" class="footer-develop-a">Material</a></div>
            </div>
        </div>
    
</footer>


                    <!-- Import JS File -->

    <script>lsloader.load("lazyload_js","/rubin-blog/js/lazyload.min.js?wgjW/HuQG9JDgvPDPoRAng==", true)</script>



    <script>lsloader.load("js_js","/rubin-blog/js/js.min.js?LT4t6iE6m8TO1BLGGiNJqA==", true)</script>



    <script>lsloader.load("np_js","/rubin-blog/js/nprogress.js?pl3Qhb9lvqR1FlyLUna1Yw==", true)</script>


<script type="text/ls-javascript" id="NProgress-script">
    NProgress.configure({
        showSpinner: true
    });
    NProgress.start();
    $('#nprogress .bar').css({
        'background': '#29d'
    });
    $('#nprogress .peg').css({
        'box-shadow': '0 0 10px #29d, 0 0 15px #29d'
    });
    $('#nprogress .spinner-icon').css({
        'border-top-color': '#29d',
        'border-left-color': '#29d'
    });
    setTimeout(function() {
        NProgress.done();
        $('.fade').removeClass('out');
    }, 800);
</script>













<!-- UC Browser Compatible -->
<script>
	var agent = navigator.userAgent.toLowerCase();
	if(agent.indexOf('ucbrowser')>0) {
		document.write('<link rel="stylesheet" href="/rubin-blog/css/uc.css">');
	   alert('由于 UC 浏览器使用极旧的内核，而本网站使用了一些新的特性。\n为了您能更好的浏览，推荐使用 Chrome 或 Firefox 浏览器。');
	}
</script>

<!-- Import prettify js  -->



<!-- Window Load -->
<!-- add class for prettify -->
<script type="text/ls-javascript" id="window-load">
    $(window).on('load', function() {
        // Post_Toc parent position fixed
        $('.post-toc-wrap').parent('.mdl-menu__container').css('position', 'fixed');
    });

    
    
</script>

<!-- MathJax Load-->


<!-- Bing Background -->


<script type="text/ls-javascript" id="lazy-load">
    // Offer LazyLoad
    queue.offer(function(){
        $('.lazy').lazyload({
            effect : 'show'
        });
    });

    // Start Queue
    $(document).ready(function(){
        setInterval(function(){
            queue.execNext();
        },200);
    });
</script>

<!-- Custom Footer -->



<script>
    var copyrightNow = new Date().getFullYear();
    var textContent = document.querySelector('span[year]')

    copyrightSince = 0000;
    if (copyrightSince === copyrightNow||copyrightSince === 0000) {
        textContent.textContent = copyrightNow
    } else {
        textContent.textContent = copyrightSince + ' - ' + copyrightNow
    }

    (function(){
        var scriptList = document.querySelectorAll('script[type="text/ls-javascript"]')

        for (var i = 0; i < scriptList.length; ++i) {
            var item = scriptList[i];
            lsloader.runInlineScript(item.id,item.id);
        }
    })()
console.log('\n %c © Material Theme | Version: 1.5.6 | https://github.com/viosey/hexo-theme-material %c \n', 'color:#455a64;background:#e0e0e0;padding:5px 0;border-top-left-radius:5px;border-bottom-left-radius:5px;', 'color:#455a64;background:#e0e0e0;padding:5px 0;border-top-right-radius:5px;border-bottom-right-radius:5px;');
</script>

                </main>
            </div>
        </body>
    
    <link  href="/rubin-blog/css/viewer.min.css" rel="stylesheet">
    <script src="/rubin-blog/js/viewer.min.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
        //默认设置， 可以根据个人需求和喜好进行配置
        //详细参考官方说明
        Viewer.setDefaults({
            //设置初始缩放 default: 1
            zoomRatio : [0.5],
            //设置滚轮缩放比率 default: 0.1
            show: function () {
              this.viewer.zoomTo(0.5);
            },
          });
        //获得content中所有的图片， 不同主题可能有所不同
        //为了和其他的图片区别开来 所以在markdown中插入图片的时候使用独特的记号
        var imageList = document.getElementsByTagName("img");
        //将获取到的HTMLCollections转化成Array
        var imageArray = new Array();
        Array.prototype.forEach.call(imageList, element => {
          imageArray.push(element);
        });
        //设置每个图片成为图片组
        Array.prototype.forEach.call(imageList, element => {
          var viewer1 = new Viewer(element);
          viewer1.images = imageArray;
          viewer1.length = imageArray.length;
        });
    </script>
</html>
